﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using A2DFramework.EsbService.DistributedEsb.Configuration;
using System.Web;
using A2DFramework.XmlService;
using A2DFramework.InstanceService;
using A2DFramework.QueueService;

namespace A2DFramework.EsbService.DistributedEsb
{
    public class CommandBusDistributer<TCmd, TResult>
        where TCmd : EsbRequestWrapper
        where TResult : EsbResponseWrapper
    {
        private string handlerId;
        private string sessionId;
        private string sessionItems;
        private EventWaitHandle signal;
        private TResult result;
        public event CommandResultCatchedDelegate<TResult> ResultCatached;
        private IQueue queueSrv;

        public CommandBusDistributer()
        {
            signal = new EventWaitHandle(true, EventResetMode.ManualReset);
            sessionId = string.Empty;
            sessionItems = string.Empty;
            queueSrv = ObjectFactory.Resolve<IQueue>();

            if (DistributedEsbConfiguration.WebSessionSupport)
            {
                if (HttpContext.Current != null)
                {
                    sessionId = HttpContext.Current.Session.SessionID;
                    List<KeyValuePair<string, object>> kvs = new List<KeyValuePair<string, object>>();
                    foreach (string key in HttpContext.Current.Session.Keys)
                    {
                        object o = HttpContext.Current.Session[key];
                        KeyValuePair<string, object> kv = new KeyValuePair<string, object>(key, o);
                        kvs.Add(kv);
                    }
                    sessionItems = SerializeUtils.BinarySerialize2Base64(kvs);
                }
            }
        }

        public void SendRequest(TCmd cmd)
        {
            this.result = null;
            this.handlerId = string.Empty;

            cmd.SessionID = this.sessionId;
            cmd.SessionItems = this.sessionItems;

            IQueue srv = ObjectFactory.Resolve<IQueue>();
            Message<TCmd> msg = new Message<TCmd>();
            msg.Body = cmd;
            srv.SendRequest2Worker<TCmd>(msg);
            this.handlerId = msg.ID;

            //check response in background thread
            ThreadPool.QueueUserWorkItem(new WaitCallback(CheckResponseQueue));

            //int workerThreads, completionPortThreads;
            //ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
            //Console.WriteLine(string.Format("workerThreads: {1}, completionPortThreads: {1}", workerThreads, completionPortThreads));
        }

        public TResult WaitResponse()
        {
            return WaitResponse(-1);
        }
        public TResult WaitResponse(int timeoutMilliseconds)
        {
            if (this.result != null)
                return this.result;

            signal.Reset();

            if (timeoutMilliseconds <= 0)
                signal.WaitOne();
            else
                signal.WaitOne(timeoutMilliseconds);

            if (this.result == null)
                throw new TimeoutException("Timeout");

            return this.result;
        }
        public bool CheckResponse()
        {
            return this.result != null;
        }


        private void CheckResponseQueue(object state)
        {
            string msgId = this.handlerId;

            //Console.WriteLine("CheckResponseQueue start: "+ msgId);

            this.result= queueSrv.GetResponseFromWorker<TResult>(msgId);

            //Console.WriteLine("CheckResponseQueue end: " + msgId);

            signal.Set();

            if (this.result != null)
                OnResultCatached(this.result);
        }

        private void OnResultCatached(TResult result)
        {
            if (this.ResultCatached != null)
                this.ResultCatached.Invoke(result);
        }
    }
}